#!/usr/bin/python2.7

import Graph
import Drawable
from Vector2 import *
from Dependency import *
from Graph import *

import cairo


class GraphicalGraphException:
    def __init__(self, error):
        self.error = error

class GraphicalGraph(DependencyGraph, Drawable.Drawable, gobject.GObject):

    def __init__(self):
        self.__gobject_init__()
        DependencyGraph.__init__(self)
        Drawable.Drawable.__init__(self)
        self.graphics = {}

    def addNode(self, value, position=Vector2(0, 0), size=Vector2(20, 20), hidden=False):
        if not isinstance(value, DependencyNode):
            raise GraphicalGraphException("Not a DependencyNode")
        Graph.addNode(self, value)
        self.graphics[value] = [position, size, hidden]
        

    def removeNode(self, node):
        del self.graphics[node]
        Graph.removeNode(self, node)
   
    def setHidden(self, node, hidden):
       self.graphics[node][2] = hidden
   
        
    def getNodePosition(self, node):
        position = self.graphics[node][0]
        if not node is None:
            return position
        else:
            raise GraphException("The node has not been added to the graph yet")

    def getNodeSize(self, node):
        position = self.graphics[node][1]
        if not node is None:
            return position
        else:
            raise GraphException("The node has not been added to the graph yet")


    def moveNodeTo(self, node, new_position):
        self.graphics[node][0] = new_position
        

    def draw_cairo(self, cr, absolute_position):
        abs_x = int(absolute_position.getX())
        abs_y = int(absolute_position.getY())	
        grad = cairo.LinearGradient(0.0, 0.0, 350.0, 350.0)
       
        grad.add_color_stop_rgba(1, 0, 0, 0, 1)
        grad.add_color_stop_rgba(1, 1, 0, 0, 1)    
        
        for (position, size) in self.graphics.values():
            x, y = position.getValue()
            w, h = size.getValue()	
            cr.rectangle(int(abs_x + x), int(abs_y+y), int(w), int(h))
            cr.fill()
        #cr.set_source(grad)
        cr.set_line_width(0.5)
        for (node1, node2) in self.edges:
            position1 = self.getNodePosition(node1)
            position2 = self.getNodePosition(node2)
            cr.move_to(position1.getX(), position1.getY())
            cr.line_to(position2.getX(), position2.getY()) 
            window.draw_line(gc, abs_x + position1.getX(), abs_y + position1.getY(), abs_x + position2.getX(), abs_y + position2.getY())
            cr.stroke()
        
    def draw(self, drawing_area, absolute_position, selected_nodes, withLine=True):
        abs_x = int(absolute_position.getX())
        abs_y = int(absolute_position.getY())
        normal_gc = drawing_area.window.new_gc()
        normal_gc.set_foreground(drawing_area.window.get_colormap().alloc_color("#000000")) 
        edges_gc = drawing_area.window.new_gc()
        edges_gc.set_foreground(drawing_area.window.get_colormap().alloc_color("#000000")) 
        
        #orange
        selected_nodes_gc = drawing_area.window.new_gc()
        selected_nodes_gc.set_foreground(drawing_area.window.get_colormap().alloc_color("#FF8000"))
        
        # Red
        parent_nodes_gc = drawing_area.window.new_gc()
        parent_nodes_gc.set_foreground(drawing_area.window.get_colormap().alloc_color("#FF0000"))
        
        #green
        children_nodes_gc = drawing_area.window.new_gc()
        children_nodes_gc.set_foreground(drawing_area.window.get_colormap().alloc_color("#00FF00"))
        
        copy = self.graphics
        drawn = {}
        for (node, (position, size, hidden)) in copy.items():
            if not self.hiddenNode(node):
                x, y = position.getValue()
                w, h = size.getValue()
                if not node in drawn.keys():
                    if node in selected_nodes:
                        drawing_area.window.draw_rectangle(selected_nodes_gc, True, int(abs_x + x), int(abs_y+y), int(w), int(h))
                        drawn[node] = True
                        parents = self.parents(node)
                        children = self.children(node)
                        
                        # Draw parents in red
                        #print len(parents), " / ", len(children)
                        #for parent in parents:
                            # if not parent in drawn.keys():
                                # parent_x, parent_y = copy[parent][0].getValue()
                                # parent_w, parent_h = copy[parent][1].getValue()
                                # drawing_area.window.draw_rectangle(parent_nodes_gc, True, int(abs_x + parent_x), int(abs_y+parent_y), int(parent_w), int(parent_h))
                                # drawn[parent] = True
                        if len(selected_nodes) == 1  :      
                            for child in children:
                                if not child in drawn.keys() and not self.hiddenNode(child):
                                    child_x, child_y = copy[child][0].getValue()
                                    child_w, child_h = copy[child][1].getValue()
                                    drawing_area.window.draw_rectangle(children_nodes_gc, True, int(abs_x+child_x), int(abs_y+child_y), int(child_w), int(child_h))
                                    drawn[child] = True
                    else:
                        drawing_area.window.draw_rectangle(normal_gc, True, int(abs_x + x), int(abs_y+y), int(w), int(h))
        
        # draw the lines
        if withLine:
            for edge in self.edges:
                # Do not display edge if the edge is hidden
                if not self.hiddenEdge(edge):
                    node1, node2 = edge
                    position1 = self.getNodePosition(node1)
                    size1 = self.getNodeSize(node1)
                    position2 = self.getNodePosition(node2)
                    size2 = self.getNodeSize(node2)
                    point1_x = int(position1.getX() + size1.getX() / 2.0)
                    point1_y = int(position1.getY() + size1.getY() / 2.0)
                    point2_x = int(position2.getX() + size2.getX() / 2.0)
                    point2_y = int(position2.getY() + size2.getY() / 2.0)
                    drawing_area.window.draw_line(edges_gc, abs_x + point1_x, abs_y + point1_y, abs_x + point2_x, abs_y + point2_y)


    def nodesUnderPoint(self, point):
        nodes = []
        for (node, (position, size, hidden)) in self.graphics.items():
            if point >= position and point < position + size and not hidden:
                nodes.append(node)
        return nodes
        
    def nodesInBox(self, point1, point2):
        nodes = []
        for (node, (position, size, hidden)) in self.graphics.items():
            rect = Rectangle(point1, point2)
            if position >= rect.getPointA() and position + size <= rect.getPointB() and not hidden:
                nodes.append(node)
        return nodes

    def hiddenNode(self, node):
        return self.graphics[node][2]
        
    def hiddenEdge(self, edge):
        node1, node2 = edge
        return self.hiddenNode(node1) or self.hiddenNode(node2)
    
    
    def toXml(self, increment):
        xml = " " * increment + "<graphicalgraph>\n"
        xml += " " * increment + " <nodes>\n"
        for (index, node) in self.nodes.items():
            position, size, hidden = self.graphics[node]
            xml += " " * increment +"  <graphicalnode index=\"" + str(index) + "\" x=\""+ str(int(position.getX())) + "\" y=\""+ str(int(position.getY())) + "\" width=\""+ str(int(size.getX())) + "\" height=\""+ str(int(size.getY())) + "\" hidden=\""+str(hidden) + "\">\n"
            xml += node.toXml(increment + 2)
            xml += " " * increment +"  </graphicalnode>\n"
            
        xml += " " * increment + " </nodes>\n"
       
        xml += " " * increment + " <edges>\n"
        
        for edge in self.edges:
            node1 = edge[0]
            node2 = edge[1]
            parent_index = [index for index, node in self.nodes.items() if node == node1][0]
            child_index = [index for index, node in self.nodes.items() if node == node2][0]
            xml += " " * increment + "  <edge parent=\""+str(parent_index)+"\" child=\""+str(child_index)+"\"/>\n"
                    
        xml += " " * increment + " </edges>\n"
        xml += " " * increment + "</graphicalgraph>\n"
        return xml
        
    def fromXml(self, xml):
        doc = parseString(xml)
        graphicalgraphElement = doc.getElementsByTagName("graphicalgraph")[0]
        nodes = graphicalgraphElement.getElementsByTagName("graphicalnode")
        xmledges = graphicalgraphElement.getElementsByTagName("edge")
        
        for xmlnode in nodes:
            index = int(xmlnode.getAttribute("index"))
            x = int(xmlnode.getAttribute("x"))
            y = int(xmlnode.getAttribute("y"))
            width = int(xmlnode.getAttribute("width"))
            height = int(xmlnode.getAttribute("height"))
            hidden = xmlnode.getAttribute("hidden") == "True"
            position = Vector2(x, y)
            size = Vector2(width, height)
            
            node = DependencyNode("")
            node.fromXml(xmlnode.toxml())
            self.index = index
            self.addNode(node, position=position, size=size, hidden=hidden)
            self.index = self.index + 1
            
        for xmledge in xmledges:
            parent_index = int(xmledge.getAttribute("parent"))
            child_index = int(xmledge.getAttribute("child"))
            self.addEdge(self.nodes[parent_index], self.nodes[child_index])